import { computed, createApp, defineComponent, getCurrentInstance, onBeforeUnmount, onMounted, PropType, reactive, ref, provide, inject } from "vue";
import './index.scss';

interface DropdownOption {
  render: () => JSX.Element;
  reference: MouseEvent | HTMLElement;
}
const DropownServiceProvider = (() => {
  const DROPDOWN_SERVICE_PROVIDE = '@@DROPDOWN_SERVICE_PROVIDE'
  return {
    provide : (handler: {onClick: () => void}) => {
      provide(DROPDOWN_SERVICE_PROVIDE, handler)
    },
    inject: () => inject(DROPDOWN_SERVICE_PROVIDE) as {onClick: () => void}
  }
})()
const Dorpdown = defineComponent({
  props: {
    option: {
      type: Object as PropType<DropdownOption>,
      required: true
    }
  },

  setup(props) {

    const ctx = getCurrentInstance()
    const state = reactive({
      showFlag: false,
      option: props.option,
      left:0,
      top: 0
    })

    const el = ref({} as HTMLDivElement)
    
    const methods = {
      service: (option: DropdownOption) => {
        state.option = option
        /** 重载 */
        if('addEventListener' in state.option.reference){
          const {top, left, height} = state.option.reference.getBoundingClientRect()
          state.left = left
          state.top = top + height
        }else{
          const {clientX, clientY} = state.option.reference
          state.left = clientX,
          state.top = clientY
        }
        methods.show()
      },
      show: () => {
        state.showFlag = true
      },
      hide: () => {
        state.showFlag = false
      },
      onMousedown: (e: MouseEvent) => {
        if(!(el.value).contains(e.target as HTMLDivElement)){
          methods.hide() 
        }
      }
    }
    onMounted(() => window.addEventListener('mousedown', methods.onMousedown, true))
    onBeforeUnmount(() => window.removeEventListener('mousedown', methods.onMousedown, true))

    DropownServiceProvider.provide({onClick: methods.hide})
    const styles = computed(() => ({
      left: `${state.left}px`,
      top: `${state.top}px`
    }))

    const classes = computed(() => [
      'dropdown-service',
      {
        'dropdown-service-show':state.showFlag
      }
    ])

    Object.assign(ctx?.proxy, methods)
    return () => (
      <div class={classes.value} style={styles.value} ref={el}>
        {props.option.render()}
      </div>
    )
  }
})

export const $$dropdown = (() => {
  let ins: any = null
  return (option: DropdownOption) => {
    if (!ins) {
      const el = document.createElement('div');
      document.body.appendChild(el)
      ins = createApp(Dorpdown, { option }).mount(el)
    }
    ins.service(option)
  }
})()

export const DropdownOption = defineComponent({
  props:{
    label: String,
    icon: String
  },  
  emits:{
    onClick: (e: MouseEvent) => true
  },
  setup(props, ctx){
    const {onClick: DropdownInject} = DropownServiceProvider.inject()
    const methods = {
      click: (e: MouseEvent) => {
        ctx.emit('onClick', e)
        DropdownInject()
      }
    }
    return () => (
      <div class="dropdown-service-option" onClick={methods.click}>
        <i class={`iconfont ${props.icon} dropdown-service-option-icon`}/>
        <span class="dropdown-service-option-label">{props.label}</span>
      </div>
    )
  }
})